/**
 * Copyright(c) by JNet Smartware Group.
 * All rights reserved. Not distribution or redistribution without
 * any permission of JNet Smartware Group.
 * 
 * @author thuc
 * 2010/03/15
 */
package vn.smartware.co.java.net.commons;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;


/**
 * @author thuctt
 *
 */
public class CalendarUtils {
	
	/**
	 * 
	 * Private Function - Start
	 * @param date 
	 * @return int
	 */
	private static int getYearOfString(String date) {
		return Integer.parseInt(date.substring(0, 4));
	}
	private static int getMonthOfString(String date) {
		return Integer.parseInt(date.substring(4, 6)) - 1;
	}
	private static int getDayOfString(String date) {
		if (null == date) {
			throw new NullPointerException();
		}
		if (!isNum(date)) {
			throw new IllegalArgumentException();
		}
		if (date.length() == 8) {
			return Integer.parseInt(date.substring(6, 8));
		}
		throw new IllegalArgumentException();
	}
	private static boolean isNum(String aDate) {
		char charData = ' ';

		if (null == aDate) {
			throw new NullPointerException();
		}
		if (aDate.length() == 0) {
			return false;
		}
		for (int i = 0; i < aDate.length(); i++) {
			charData = aDate.charAt(i);
			if (((charData < '0') || (charData > '9'))) {
				return false;
			}
		}
		return true;
	}
	private static Calendar _getCalendar(int year, int month, int day) {
		GregorianCalendar cl = new GregorianCalendar();
		cl.set(Calendar.YEAR, year);
		cl.set(Calendar.MONTH, month);
		cl.set(Calendar.DAY_OF_MONTH, day);
		return cl;
	}
	private static Calendar _getCalendarOfDate(String date) {
		return _getCalendar(getYearOfString(date), getMonthOfString(date),
				getDayOfString(date));

	}

	/**
	 * 
	 * Private function - End
	 */

	/**
	 * Get system Date
	 * 
	 * @return String
	 */
	public static Date getToday() {
		// get system Date
		GregorianCalendar cl = new GregorianCalendar();
		cl.set(Calendar.HOUR_OF_DAY, 0);
		cl.set(Calendar.MINUTE, 0);
		cl.set(Calendar.SECOND, 0);
		cl.set(Calendar.MILLISECOND, 0);
		return cl.getTime();
	}

	/**
	 * Get system Time
	 * 
	 * @return String
	 */
	public static Date getNow() {
		// get system Time
		return new Date();
	}

	/**
	 * Get system data or system time with format style
	 * 
	 * @param sFormat
	 *            yyyyMMdd HHmmss
	 * @return String
	 */
	public static String getNow(String sFormat) {
		// Get System Date or System Time By sFormat
		if (null == sFormat) {
			throw new NullPointerException();
		}
		if (sFormat.trim().equals("")) {
			return "";
		}
		return new SimpleDateFormat(sFormat).format(new Date());
	}

	/**
	 * Get Num Weeks of Month
	 * 
	 * @param aYearMonth
	 * @return int
	 */
	public static int getWeeks(String aYearMonth) {
		if (null == aYearMonth) {
			throw new NullPointerException();
		}
		aYearMonth = aYearMonth.trim();
		if (isValidMonth(aYearMonth)) {
			int dd = getMaxDayOfMonth(aYearMonth);
			aYearMonth = aYearMonth + String.valueOf(dd);
			Calendar cl = _getCalendarOfDate(aYearMonth);
			cl.setFirstDayOfWeek(Calendar.MONDAY);
			return cl.get(Calendar.WEEK_OF_MONTH);
		}
		throw new IllegalArgumentException();
	}
	/**
	 * Get Num Weeks of Day in Month
	 * 
	 * @param aDate
	 * @return int
	 */
	public static int getWeekNumber(Date aDate) {
		
		if (Utils.isNullEmpty(aDate)) {
			throw new NullPointerException();
		}

		String date = new SimpleDateFormat("yyyyMMdd").format(aDate);
		Calendar cl = _getCalendarOfDate(date);
		cl.setFirstDayOfWeek(Calendar.MONDAY);
		return cl.get(Calendar.WEEK_OF_MONTH);

	}

	/**
	 * Add Day
	 * 
	 * @param aDate
	 * @param aDays
	 * @return Date after Add aDate + aDays
	 */
	public static Date addDays(Date aDate, int aDays) {
		
		if (Utils.isNullEmpty(aDate)) {
			throw new NullPointerException();
		}

		GregorianCalendar cl = new GregorianCalendar();
		cl.setTime(aDate);
		cl.set(Calendar.DATE, cl.get(Calendar.DATE) + aDays);
		return cl.getTime();

	}

	/**
	 * Add Weeks
	 * 
	 * @param aDate
	 * @param aWeeks
	 * @return Date after Add aDate + aWeeks
	 */
	public static Date addWeeks(Date aDate, int aWeeks) {
		
		if (Utils.isNullEmpty(aDate)) {
			throw new NullPointerException();
		}

		GregorianCalendar cl = new GregorianCalendar();
		cl.setTime(aDate);
		cl.set(Calendar.DATE, cl.get(Calendar.DATE) + (aWeeks * 7));
		return cl.getTime();

	}
	/**
	 * Add Months
	 * 
	 * @param aDate
	 * @param aMonths
	 * @return Date after Add aDate + aMonths
	 */
	public static Date addMonths(Date aDate, int aMonths) {
		
		if (Utils.isNullEmpty(aDate)) {
			throw new NullPointerException();
		}
		GregorianCalendar cl = new GregorianCalendar();
		cl.setTime(aDate);

		int byyyy = cl.get(Calendar.YEAR) + 1900;
		int bmm = byyyy * 12 + cl.get(Calendar.MONTH) + 1;
		int dd = cl.get(Calendar.DATE);
		int mm = bmm + aMonths;
		int ayyyy = mm / 12;
		int amm = mm % 12;
		String yyyymm = new StringBuffer().append(ayyyy).toString();
		if (amm == 0) {
			ayyyy--;
			yyyymm = new StringBuffer().append(ayyyy).toString();
			yyyymm += "12";
			amm = 12;
		} else if (amm >= 10) {
			yyyymm += new StringBuffer().append(amm).toString();
		} else {
			yyyymm += "0" + new StringBuffer().append(amm).toString();
		}
		int Maxdd = getMaxDayOfMonth(yyyymm);
		if (dd > Maxdd) {
			cl.set(Calendar.DATE, (Maxdd));

		} else {
			cl.set(Calendar.DATE, (dd));
		}
		cl.set(Calendar.MONTH, (amm - 1));
		cl.set(Calendar.YEAR, (ayyyy - 1900));
		return cl.getTime();
	}
	/**
	 * Caculate duration day between two Date
	 * 
	 * @param aStartDate
	 * @param anEndDate
	 * @param isStartDateContained
	 * @param isEndDateContained
	 * @return Num day between Two Date
	 */
	public static int getDays(Date aStartDate, Date anEndDate,
			boolean isStartDateContained, boolean isEndDateContained) {
		
		int numDay = 0;
		if ((Utils.isNullEmpty(aStartDate))
				|| (Utils.isNullEmpty(anEndDate))) {
			throw new NullPointerException();
		}

		long ms = anEndDate.getTime() - aStartDate.getTime();
		numDay = Math.round(ms / (24 * 60 * 60 * 1000));
		if (numDay < 0) {
			numDay = 0;
		} else if (numDay == 0) {
			if ((isStartDateContained == false)
					&& (isEndDateContained == false)) {
				numDay = 0;
			} else {
				numDay = 1;
			}
		} else {
			numDay++;
			if (isStartDateContained == false) {
				numDay--;
			}
			if (isEndDateContained == false) {
				numDay--;
			}
		}

		return numDay;
	}
	/**
	 * Caculate duration Month between two Date
	 * 
	 * @param aStartYearMonth
	 * @param anEndYearMonth
	 * @param isStartYearMonthContained
	 * @param isEndYearMonthContained
	 * @return Num Month between Two Date
	 */
	public static int getMonths(String aStartYearMonth, String anEndYearMonth,
			boolean isStartYearMonthContained, boolean isEndYearMonthContained) {
		
		if ((null == aStartYearMonth) || (null == anEndYearMonth)) {
			throw new NullPointerException();
		}
		int numMonth = 0;
		aStartYearMonth = aStartYearMonth.trim();
		anEndYearMonth = anEndYearMonth.trim();
		if ((aStartYearMonth.length() == 6) && (anEndYearMonth.length() == 6)) {
			if (isValidMonth(aStartYearMonth) == true
					&& isValidMonth(anEndYearMonth) == true) {
				int SumStartMonth = (getYearOfString(aStartYearMonth) * 12)
						+ (Integer.parseInt(aStartYearMonth.substring(4, 6)));
				int SumEndMonth = (getYearOfString(anEndYearMonth) * 12)
						+ (Integer.parseInt(anEndYearMonth.substring(4, 6)));
				numMonth = SumEndMonth - SumStartMonth;
				if (numMonth == 0) {
					if ((isStartYearMonthContained == false)
							&& (isEndYearMonthContained == false)) {
						numMonth = 0;
					} else {
						numMonth = 1;
					}
				} else if (numMonth < 0) {
					numMonth = 0;
				} else {
					numMonth++;
					if (isStartYearMonthContained == false) {
						numMonth--;
					}
					if (isEndYearMonthContained == false) {
						numMonth--;
					}
				}
			}
		}
		return numMonth;
	}
	/**
	 * 
	 * @param aYearMonth
	 * @return The Last Date of Month
	 */

	public static Date getLastDateOfMonth(String aYearMonth) {

		if (null == aYearMonth) {
			throw new NullPointerException();
		}
		aYearMonth = aYearMonth.trim();
		if (!isNum(aYearMonth)) {
			throw new IllegalArgumentException();
		}
		if (aYearMonth.length() == 6) {
			GregorianCalendar cl = new GregorianCalendar();
			cl.set(Calendar.DAY_OF_MONTH, 1);
			cl.set(Calendar.YEAR, getYearOfString(aYearMonth));
			cl.set(Calendar.MONTH, getMonthOfString(aYearMonth));
			cl.set(Calendar.DAY_OF_MONTH, cl
					.getActualMaximum(Calendar.DAY_OF_MONTH));
			cl.set(Calendar.HOUR_OF_DAY, 0);
			cl.set(Calendar.MINUTE, 0);
			cl.set(Calendar.SECOND, 0);
			cl.set(Calendar.MILLISECOND, 0);
			return cl.getTime();
		}
		throw new IllegalArgumentException();
	}
	/**
	 * 
	 * Get last date of month.
	 * @param aDate
	 * @return last date of month
	 * @author tranghv
	 */
	public static Date getLastDateOfMonth(Date aDate) {
		if (null == aDate) {
			throw new NullPointerException();
		}
		String sDate = Utils.formatDate(aDate,"yyyyMM");
		return getLastDateOfMonth(sDate);
	}
	
	/**
	 * Private function to Check Valid Date
	 * 
	 * @param aDate
	 *            yyyyMMdd, aDateFormat yyyyMMdd
	 * @param aDateFormat 
	 * @return boolean
	 * @throws ParseException 
	 */
	private static boolean _isValidDate(String aDate, String aDateFormat)
			throws ParseException {
		if (null == aDate || null == aDateFormat) {
			throw new NullPointerException();
		}
		if ("" == aDate || "" == aDateFormat) {
			return false;
		}
		if (aDate.length() != aDateFormat.length()) {
			return false;
		}
		DateFormat df = new SimpleDateFormat(aDateFormat);
		return aDate.equals(df.format(df.parse(aDate)));
	}

	/**
	 * Check Valid Date
	 * 
	 * @param aDate
	 *            yyyyMMdd
	 * @param aDateFormat
	 *            yyyyMMdd
	 * @return boolean
	 */
	public static boolean isValidDate(String aDate, String aDateFormat) {
		boolean Valid = false;
		if ((null == aDate) || (null == aDateFormat)) {
			throw new NullPointerException();
		}
		try {
			Valid = _isValidDate(aDate, aDateFormat);
		} catch (ParseException e) {
			// Do nothing
			// e.printStackTrace();
			
		}
		return Valid;
	}
	/**
	 * Check Valid Month
	 * 
	 * @param aYearMonth
	 *            yyyyMM
	 * @return boolean
	 */
	public static boolean isValidMonth(String aYearMonth) {
		boolean Valid = false;
		if (null == aYearMonth) {
			throw new NullPointerException();
		}
		if (aYearMonth.length() == 0) {
			return Valid;
		}
		aYearMonth = aYearMonth.trim() + "01";
		try {
			Valid = _isValidDate(aYearMonth, "yyyyMMdd");
		} catch (ParseException e) {
			// e.printStackTrace();
			// Do Nothing
			
		}
		return Valid;
	}
	/**
	 * Check leap year
	 * 
	 * @param aYear
	 *            int
	 * @return boolean
	 */
	public static boolean isLeapYear(int aYear) {
		// Test parameter year is Leap Year
		if (aYear < 0) {
			throw new IllegalArgumentException();
		}
		GregorianCalendar cl = new GregorianCalendar();
		return cl.isLeapYear(aYear);
	}
	/**
	 * Check leap year
	 * 
	 * @param aYear
	 *            String
	 * @return boolean
	 */
	public static boolean isLeapYear(String aYear) {
		aYear = aYear.trim();
		if (null == aYear) {
			throw new NullPointerException();
		}
		int year = Integer.parseInt(aYear);
		if (year < 0) {
			throw new IllegalArgumentException();
		}
		GregorianCalendar cl = new GregorianCalendar();

		return cl.isLeapYear(year);
	}
	/**
	 * Get Name day of Date
	 * 
	 * @param aDate
	 * @return name of Date
	 */
	public static String getDayOfTheWeek(Date aDate) {

		if (Utils.isNullEmpty(aDate)) {
			throw new NullPointerException();
		}

		Calendar cl = new GregorianCalendar();
		cl.setTime(aDate);
		int dayOfWeek = cl.get(Calendar.DAY_OF_WEEK);
		switch (dayOfWeek) {
			case Calendar.SUNDAY :
				return "sunday";
			case Calendar.MONDAY :
				return "monday";
			case Calendar.TUESDAY :
				return "tuesday";
			case Calendar.WEDNESDAY :
				return "wednesday";
			case Calendar.THURSDAY :
				return "thursday";
			case Calendar.FRIDAY :
				return "friday";
			case Calendar.SATURDAY :
				return "saturday";
			default :
				return " ";
		}

	}
	
	/**
	 * get max day of month
	 * @param anYearMonth
	 * @return int
	 */
	public static int getMaxDayOfMonth(String anYearMonth) {
		if (null == anYearMonth) {
			throw new NullPointerException();
		}
		if (anYearMonth.length() == 6) {
			GregorianCalendar calendar = new GregorianCalendar();
			calendar.set(Calendar.DAY_OF_MONTH, 1);
			calendar.set(Calendar.YEAR, Integer.parseInt(anYearMonth.substring(
					0, 4)));
			calendar.set(Calendar.MONTH, Integer.parseInt(anYearMonth
					.substring(4, 6)) - 1);
			int maxDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
			return maxDays;
		}
		throw new IllegalArgumentException();
	}

	/**
	 * 
	 * get first of month
	 * 
	 * @param aYm :
	 *            year month
	 * @return the first of date
	 * @author AIT-Damnh
	 */
	public static Date getFirstOfMonth(String aYm) {
		Date date = null;
		try {
			date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS").parse(aYm
					.substring(0, 4)
					+ "/" + aYm.substring(4, 6) + "/01 00:00:00.000");
		} catch (Exception e) {
			date = null;
		}
		return date;
	}
	
	/**
	 * 
	 * add month
	 * 
	 * @param aYm
	 * @param aMonth :
	 *            so thang phai cong/tru
	 * @return ym : year month
	 * @author AIT-Damnh
	 */
	public static String addMonth(String aYm, int aMonth) {
		Date date = getFirstOfMonth(aYm);
		if (date == null)
			return null;
		return Utils.formatDate(CalendarUtils.addMonths(date, aMonth), "yyyyMM");
	}

	/**
	 * @param date
	 * @return yyyyMM
	 */
	public static String getYm(Date date) {
		if (null!=date){
			return new SimpleDateFormat("yyyyMM").format(date);
		}
		return "";
	}
	/**
	 * compare year month
	 * 
	 * @param ym1
	 * @param ym2
	 * @return 0, if ym1=ym2,<br>
	 *         <0 if ym1<ym2,<br>
	 *         >0 if ym1>ym2
	 */
	public static int compareYm(String ym1, String ym2) {
		Date d1 = getFirstOfMonth(ym1);
		Date d2 = getFirstOfMonth(ym2);
		return d1.compareTo(d2);
	}

	/**
	 * Get begin of date (time 00:00:00)
	 * @param date
	 * @return date+time
	 */
	public static Date getStartDateTime(Date date) {
		if (date == null) {
			return null;
		}
		Calendar calen = Calendar.getInstance();
		calen.setTime(date);
		// set start hour, minus, second of day
		calen.set(Calendar.HOUR_OF_DAY, 0);
		calen.set(Calendar.MINUTE, 0);
		calen.set(Calendar.SECOND, 0);

		return calen.getTime();
	}
	
	/**
	 * Get end of date (time: 23:59:59)
	 * @param date
	 * @return date+time
	 */
	public static Date getEndDateTime(Date date) {
		if (date == null) {
			return null;
		}
		Calendar calen = Calendar.getInstance();
		calen.setTime(date);
		// set end hour, minus, second of day
		calen.set(Calendar.HOUR_OF_DAY, 23);
		calen.set(Calendar.MINUTE, 59);
		calen.set(Calendar.SECOND, 59);

		return calen.getTime();
	}
}